/**
 * @p.md
 * 
 * 此示例展示如何使用 Bun 内置 SQLite 提供者来持久化 Actor 状态
 */

import {
  ActorSystem,
  Props,
  PID
} from '../src/actor/index.js';

import {
  PersistentActor,
  RecoverContext,
  CommandContext,
  EventType,
  PersistenceFactory,
  RecoveryStrategy,
  BunSQLiteProvider
} from '../src/persistence/index.js';

/**
 * 计数器命令类型
 */
enum CounterCommandType {
  Increment = 'increment',
  Decrement = 'decrement',
  GetCount = 'get-count',
  Reset = 'reset',
  CreateSnapshot = 'create-snapshot'
}

/**
 * 计数器事件类型
 */
enum CounterEventType {
  Incremented = 'incremented',
  Decremented = 'decremented',
  Reset = 'reset'
}

/**
 * 计数器命令接口
 */
interface CounterCommand {
  type: CounterCommandType;
  amount?: number;
}

/**
 * 计数器事件接口
 */
interface CounterEvent {
  type: CounterEventType;
  amount?: number;
}

/**
 * 计数器状态接口
 */
interface CounterState {
  count: number;
}

/**
 * 持久化计数器 Actor
 */
class PersistentCounterActor extends PersistentActor {
  // 持久化 ID
  private _persistenceId: string;
  
  // 当前计数
  private count: number = 0;
  
  // 事件索引
  private eventIndex: number = 0;
  
  // 最后快照索引
  private lastSnapshotIndex: number = 0;
  
  // 快照间隔（事件数量）
  private readonly snapshotInterval: number = 10;
  
  /**
   * 构造函数
   * @param id 计数器 ID
   */
  constructor(id: string) {
    super();
    this._persistenceId = `counter-${id}`;
  }
  
  /**
   * 获取持久化 ID
   */
  get persistenceId(): string {
    return this._persistenceId;
  }
  
  /**
   * 恢复事件处理
   * @param context 恢复上下文
   */
  receiveRecover(context: RecoverContext): void {
    const eventType = context.eventType;
    const event = context.event;
    
    if (eventType === EventType.Snapshot) {
      // 从快照恢复状态
      const state = event as CounterState;
      this.count = state.count;
      console.log(`[${this.persistenceId}] 从快照恢复状态: count = ${this.count}`);
    } else if (eventType === EventType.Event) {
      // 从事件恢复状态
      this.applyEvent(event as CounterEvent);
      this.eventIndex = context.eventIndex;
      console.log(`[${this.persistenceId}] 从事件恢复状态: index = ${this.eventIndex}, count = ${this.count}`);
    }
  }
  
  /**
   * 命令处理
   * @param context 命令上下文
   */
  receiveCommand(context: CommandContext): void {
    const command = context.message() as CounterCommand;
    
    switch (command.type) {
      case CounterCommandType.Increment:
        const incrementAmount = command.amount || 1;
        const incrementEvent: CounterEvent = {
          type: CounterEventType.Incremented,
          amount: incrementAmount
        };
        
        // 持久化增量事件
        context.persist(incrementEvent).then(() => {
          this.eventIndex++;
          this.applyEvent(incrementEvent);
          context.respond({ count: this.count });
          
          // 检查是否需要创建快照
          this.checkSnapshot(context);
        });
        break;
        
      case CounterCommandType.Decrement:
        const decrementAmount = command.amount || 1;
        const decrementEvent: CounterEvent = {
          type: CounterEventType.Decremented,
          amount: decrementAmount
        };
        
        // 持久化减量事件
        context.persist(decrementEvent).then(() => {
          this.eventIndex++;
          this.applyEvent(decrementEvent);
          context.respond({ count: this.count });
          
          // 检查是否需要创建快照
          this.checkSnapshot(context);
        });
        break;
        
      case CounterCommandType.Reset:
        const resetEvent: CounterEvent = {
          type: CounterEventType.Reset
        };
        
        // 持久化重置事件
        context.persist(resetEvent).then(() => {
          this.eventIndex++;
          this.applyEvent(resetEvent);
          context.respond({ count: this.count });
          
          // 检查是否需要创建快照
          this.checkSnapshot(context);
        });
        break;
        
      case CounterCommandType.GetCount:
        // 只读操作，不需要持久化
        context.respond({ count: this.count });
        break;
        
      case CounterCommandType.CreateSnapshot:
        // 手动创建快照
        this.takeSnapshot(context);
        break;
        
      default:
        console.log(`[${this.persistenceId}] 未知命令: ${JSON.stringify(command)}`);
        break;
    }
  }
  
  /**
   * 应用事件到状态
   * @param event 事件
   */
  private applyEvent(event: CounterEvent): void {
    switch (event.type) {
      case CounterEventType.Incremented:
        this.count += event.amount || 1;
        console.log(`[${this.persistenceId}] 增加计数: ${event.amount || 1}, 当前计数: ${this.count}`);
        break;
        
      case CounterEventType.Decremented:
        this.count -= event.amount || 1;
        console.log(`[${this.persistenceId}] 减少计数: ${event.amount || 1}, 当前计数: ${this.count}`);
        break;
        
      case CounterEventType.Reset:
        this.count = 0;
        console.log(`[${this.persistenceId}] 重置计数`);
        break;
    }
  }
  
  /**
   * 检查是否需要创建快照
   * @param context 命令上下文
   */
  private checkSnapshot(context: CommandContext): void {
    if (this.eventIndex - this.lastSnapshotIndex >= this.snapshotInterval) {
      this.takeSnapshot(context);
    }
  }
  
  /**
   * 创建快照
   * @param context 命令上下文
   */
  private takeSnapshot(context: CommandContext): void {
    const state: CounterState = {
      count: this.count
    };
    
    context.snapshot(state).then(() => {
      this.lastSnapshotIndex = this.eventIndex;
      console.log(`[${this.persistenceId}] 创建快照: count = ${this.count}, index = ${this.eventIndex}`);
    });
  }
  
  /**
   * 恢复完成回调
   * @param context 上下文
   */
  protected onRecoveryCompleted(context: any): void {
    console.log(`[${this.persistenceId}] 恢复完成: count = ${this.count}`);
  }
}

/**
 * 运行 Bun SQLite 示例
 */
async function runBunSQLiteExample() {
  // 创建 Actor 系统
  const system = new ActorSystem();
  
  try {
    console.log('创建 Bun SQLite 提供者...');
    const dbPath = './persistence/bun-protoactor.db';
    
    // 创建 Bun SQLite 提供者
    const provider = PersistenceFactory.getInstance().createBunSQLiteProvider(dbPath);
    
    console.log('创建并启动持久化计数器 Actor...');
    const props = Props.fromProducer(() => new PersistentCounterActor('1'))
      .withRecoveryStrategy(RecoveryStrategy.Restart);
    
    // 创建计数器 Actor
    const pid = system.spawnNamed(props, 'counter-1');
    
    // 等待恢复完成
    await new Promise(resolve => setTimeout(resolve, 500));
    
    console.log('\n测试计数器操作:');
    
    // 增加计数
    await sendCommand(system, pid, { type: CounterCommandType.Increment, amount: 5 });
    
    // 增加计数
    await sendCommand(system, pid, { type: CounterCommandType.Increment, amount: 3 });
    
    // 获取计数
    await sendCommand(system, pid, { type: CounterCommandType.GetCount });
    
    // 减少计数
    await sendCommand(system, pid, { type: CounterCommandType.Decrement, amount: 2 });
    
    // 获取计数
    await sendCommand(system, pid, { type: CounterCommandType.GetCount });
    
    // 重置计数
    await sendCommand(system, pid, { type: CounterCommandType.Reset });
    
    // 获取计数
    await sendCommand(system, pid, { type: CounterCommandType.GetCount });
    
    // 再次增加计数多次，触发自动快照
    for (let i = 0; i < 11; i++) {
      await sendCommand(system, pid, { type: CounterCommandType.Increment });
    }
    
    // 获取计数
    await sendCommand(system, pid, { type: CounterCommandType.GetCount });
    
    console.log('\n测试重启后的状态恢复:');
    // 停止计数器 Actor
    await system.stopAsync(pid);
    
    // 重新创建计数器 Actor
    const newPid = system.spawnNamed(props, 'counter-1');
    
    // 等待恢复完成
    await new Promise(resolve => setTimeout(resolve, 500));
    
    // 获取恢复后的计数
    await sendCommand(system, newPid, { type: CounterCommandType.GetCount });
    
    // 再次增加计数
    await sendCommand(system, newPid, { type: CounterCommandType.Increment, amount: 10 });
    
    // 获取计数
    await sendCommand(system, newPid, { type: CounterCommandType.GetCount });
    
  } catch (error) {
    console.error('运行 Bun SQLite 示例时出错:', error);
  } finally {
    // 关闭 Actor 系统
    await system.shutdownAsync();
  }
}

/**
 * 发送命令并打印响应
 * @param system Actor 系统
 * @param pid 目标 PID
 * @param command 命令
 */
async function sendCommand(system: ActorSystem, pid: PID, command: CounterCommand): Promise<void> {
  try {
    console.log(`发送命令: ${command.type}${command.amount ? ' ' + command.amount : ''}`);
    const response = await system.requestAsync(pid, command, 1000);
    console.log(`响应: ${JSON.stringify(response)}`);
  } catch (error) {
    console.error(`发送命令 ${command.type} 失败:`, error);
  }
}

/**
 * 运行示例
 */
async function runExample() {
  await runBunSQLiteExample();
}

// 如果直接运行此文件
if (require.main === module) {
  runExample().catch(console.error);
} 